package tmt.usercenter.web.controller;

import com.alibaba.fastjson.JSON;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.BasicErrorController;
import org.springframework.boot.autoconfigure.web.DefaultErrorAttributes;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import tmt.usercenter.web.vo.HttpResult;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static org.springframework.security.web.WebAttributes.AUTHENTICATION_EXCEPTION;

/**
 * 异常信息处理，该错误处理方式优于 @ControllerAdvice，不会对资源文件进行过滤
 * Created by Stark on 2017/3/15.
 */
@Controller
@RequestMapping("/error")
public class ErrorController extends BasicErrorController {

    @Autowired
    public ErrorController(ServerProperties serverProperties) {
        super(new DefaultErrorAttributes(), serverProperties.getError());
    }

    @ResponseBody
    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
        handleErrorMessage(body);
        return ResponseEntity.status(getStatusCode(request)).body(body);
    }

    /**
     * 401 html 页面
     */
    @RequestMapping(value = "/401", produces = MediaType.TEXT_HTML_VALUE)
    public ModelAndView error401(HttpServletRequest request, HttpServletResponse response) {
        response.setStatus(getStatus(request).value());
        Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        return new ModelAndView("error/401", model);
    }

    /**
     * 401 JSON 数据
     */
    @ResponseBody
    @RequestMapping(value = "/401")
    public ResponseEntity<Map<String, Object>> error401(HttpServletRequest request) {
        Map<String, Object> body = Collections.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.APPLICATION_JSON_UTF8)));
        return new ResponseEntity<>(body, getStatus(request));
    }

    /**
     * 404 html 页面
     */
    @RequestMapping(value = "/404", produces = MediaType.TEXT_HTML_VALUE)
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        response.setStatus(getStatus(request).value());
        Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        return new ModelAndView("error/404", model);
    }

    /**
     * 404 JSON 数据
     */
    @ResponseBody
    @RequestMapping(value = "/404")
    public ResponseEntity<Map<String, Object>> error404(HttpServletRequest request) {
        Map<String, Object> body = Collections.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.APPLICATION_JSON_UTF8)));
        return new ResponseEntity<>(body, getStatus(request));
    }

    /**
     * 定义500的ModelAndView
     */
    @RequestMapping(value = "/500", produces = MediaType.TEXT_HTML_VALUE)
    public ModelAndView errorHtml500(HttpServletRequest request, HttpServletResponse response) {
        response.setStatus(getStatus(request).value());
        Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(request, true));
        return new ModelAndView("error/500", model);
    }

    /**
     * 定义500的错误JSON信息
     */
    @ResponseBody
    @RequestMapping(value = "/500")
    public ResponseEntity<Map<String, Object>> error500(HttpServletRequest request) {
        Map<String, Object> body = Collections.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.APPLICATION_JSON_UTF8)));
        HttpStatus status = getStatus(request);
        return new ResponseEntity<>(body, status);
    }

    @RequestMapping(value = "/denied", produces = MediaType.TEXT_HTML_VALUE)
    public ModelAndView accessDeniedHtml(HttpServletRequest request, HttpServletResponse response) {
        response.setStatus(getStatus(request).value());
        Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(request, true));
        return new ModelAndView("error/denied", model);
    }

    /*@RequestMapping(value = "/login")
    public ResponseEntity<Map<String, Object>> loginError(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = getStatus(request);
        Map<String, Object> body = Collections.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.APPLICATION_JSON_UTF8)));

        try {
            Object obj = request.getSession().getAttribute(AUTHENTICATION_EXCEPTION);
            if (obj != null) {
                String message = ((Exception) obj).getMessage();
                HttpResult result = HttpResult.failure(message);

                ObjectMapper mapper = new ObjectMapper();
                String json = JSON.toJSONString(result);
                TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>() {
                };
                HashMap<String, Object> o = mapper.readValue(json, typeRef);
                return new ResponseEntity<>(o, status);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return new ResponseEntity<>(body, status);
    }*/

    @ResponseBody
    @RequestMapping(value = "/denied")
    public ResponseEntity<Map<String, Object>> accessDenied(HttpServletRequest request) {
        Map<String, Object> body = Collections.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.APPLICATION_JSON_UTF8)));
        Map<String, Object> map = new HashMap<>();
        map.putAll(body);
        map.put("redirectUrl", request.getContextPath() + "/error/denied");
        HttpStatus status = getStatus(request);
        return new ResponseEntity<>(map, status);
    }


    private int getStatusCode(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR.value();
        } else {
            return statusCode;
        }
    }

    /**
     * 获取对应的 message 和 code，获取对应 code 的 message， 覆盖当前的 message。
     * TODO:处理错误信息的国际化
     */
    protected void handleErrorMessage(Map<String, Object> body) {
    }
}
